﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace StreamingServer
{
    #region 海康结构定义
    public struct PSERVER_VIDEOINFO
    {
        /// <summary>
        /// 序列号
        /// BYTE    m_datatype[64];
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        public byte[] m_datatype;
        /// <summary>
        /// 系统的通道个数
        /// BYTE    m_channum;
        /// </summary>
        public byte m_channum;
        /// <summary>
        /// 超时等待时间，1-300单位：分钟 
        /// DWORD   m_waittime;
        /// </summary>
        public int m_waittime;
        /// <summary>
        /// 目前没有使用
        /// DWORD   m_bufnum;
        /// </summary>
        public ulong m_bufnum;
    }

    //[StructLayout(LayoutKind.Sequential)]
    //public struct PSERVER_VIDEOINFO
    //{
    //    public IntPtr m_datatype;  //序列号  
    //    public byte m_channum;//系统的通道个数 
    //    public int m_waittime;//超时等待时间，1-300单位：分钟 
    //    public int m_bufnum;//目前没有使用 
    //}

    /// <summary>
    /// 通道数据类型
    /// </summary>
    public enum ChannelDataType
    {
        NORMAL = 50,
        DIALING,
        SMALLPIC
    }

    public enum SERVER_STATE
    {
        StartingUpState,
        RunningState,
        RefusingConnectionsState,
        FatalErrorState,
        ShuttingDownState
    };
    /// <summary>
    /// VC++Demo：委托内调用SetIBPMode(ChannelHandle[port],211,2,2,framerat);
    /// </summary>
    /// <param name="port"></param>
    /// <param name="framerat"></param>
    public delegate void SetIBP(int port, int framerat);

    /// <summary>
    /// VC++Demo：委托内调用CaptureIFrame(ChannelHandle[port]);
    /// </summary>
    /// <param name="port"></param>
    public delegate void MakeIFrame(int port);

    /// <summary>
    /// VC++Demo：委托内调用StartVideoCapture(ChannelHandle[port]);    
    /// </summary>
    /// <param name="nChannel"></param>
    public delegate void StartCap(int nChannel);

    /// <summary>
    /// VC++Demo：委托内调用StopVideoCapture(ChannelHandle[port]);
    /// </summary>
    /// <param name="nChannel"></param>
    public delegate void StopCap(int nChannel);

    /// <summary>
    /// 验证用户名密码
    /// </summary>
    /// <param name="username"></param>
    /// <param name="namelen"></param>
    /// <param name="password"></param>
    /// <param name="passlen"></param>
    /// <returns></returns>
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate int CheckPassword(string username, ushort namelen, string password, ushort passlen);

    /// <summary>
    /// 验证IP
    ///     VC++Demo：return 0;
    /// </summary>
    /// <param name="nChannel"></param>
    /// <param name="nIP"></param>
    /// <returns></returns>
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate int CheckIP(int nChannel, string nIP);

    #endregion

    public static class HikServerDLL
    {
        #region  海康DLL包含
          public static readonly uint WM_USER = 0x0400;       
      

        /// <summary>
        /// 启动服务端
        ///     返回true表示成功，返回false表示失败
        /// <code>
        /// BOOL  __stdcall  MP4_ServerStart(PSERVER_VIDEOINFO videoinfo); 
        /// </code>
        /// </summary>
        /// <param name="videoinfo"></param>
        /// <returns></returns>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern int MP4_ServerStart(ref PSERVER_VIDEOINFO videoinfo);

        /// <summary>
        /// 停止服务端
        ///     返回true表示成功，返回false表示失败
        /// <code>
        /// BOOL  __stdcall  MP4_ServerStop(); 
        /// </code>
        /// </summary>
        /// <returns></returns>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern bool MP4_ServerStop();

        /// <summary>
        /// 获取服务器状态
        ///     返回true表示服务器已经启动，返回false表示服务器没有启动
        /// <code>
        /// BOOL  __stdcall  MP4_ServerGetState(WORD * clientnum); 
        /// typedef unsigned short      WORD;
        /// </code>
        /// </summary>
        /// <param name="clientnum">表示当前与服务器相连的客户端数据</param>
        /// <returns></returns>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern bool MP4_ServerGetState(out ushort clientnum);

        /// <summary>
        /// 给客户端发送命令码
        ///     和函数MP4_ServerStringToClient不同，它给正连接在该通道上的所有客户端发送命令码。 
        ///     返回true表示成功，返回false表示失败 
        /// <code>
        /// BOOL  __stdcall  MP4_ServerCommandToClient(char cCommand,char nChannel); 
        /// </code>
        /// </summary>
        /// <param name="cCommand">命令码</param>
        /// <param name="nChannel">通道号</param>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern void MP4_ServerCommandToClient(int cCommand, int nChannel);

        /// <summary>
        /// 设置接收命令码的相关参数
        /// <code>
        /// void  __stdcall  MP4_ServerSetMessage(UINT nMessage,HWND hWnd); 
        /// </code>
        /// </summary>
        /// <param name="nMessage">对应接收程序的消息</param>
        /// <param name="hWnd">应用程序窗口句柄</param>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern void MP4_ServerSetMessage(uint nMessage, IntPtr hWnd);

        /// <summary>
        /// 设置是否进行IP验证。 
        ///     如果进行IP验证，调用该函数，那么每次客户端连接的时候，会调用CheckIP。 
        ///     如果不进行IP验证，不需要调用该函数。 
        ///     如果调用了MP4_ServerCheckIP之后，又想取消IP验证，只需要调用MP4_ServerCheckIP(NULL)。 
        /// <code>
        /// void  __stdcall MP4_ServerCheckIP(int(CALLBACK *CheckIP)(DWORD nChannel,char* nIP)); 
        /// CheckIP函数说明：nChannel表示通道号，nIP表示客户端的IP地址。返回0表示验证通过，返回-1 
        /// 表示验证没有通过。 
        /// </code>
        /// </summary>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern void MP4_ServerCheckIP(CheckIP cip);

        /// <summary>
        /// 设置是否进行用户身份验证。 
        ///     使用同MP4_ServerCheckIP。 
        ///     目前namelen和password两个值无效，都是50，并不表示用户名和密码的实际长度。用户名和密码必须小于50个字节。 
        /// 说明： 
        ///     1>因为开发包对用户名和密码不做任何处理，只是简单地分别发送50字节的数据到服务端，所以在验证
        ///       的实现过程中，用户可以通过设置标志位的方法来确定用户名和密码的实际长度。 
        ///     2>也可以同时进行IP验证和用户身份验证。在开发包中先进行用户身份验证，再进行IP验证。 
        /// <code>
        /// 7. void  __stdcall  MP4_ServerCheckPassword(int(CALLBACK *CheckPassword)(char *username,WORD namelen,char *password,WORD passlen)); 
        /// CheckPassword函数说明：username表示用户名，namelen表示用户名字符串长度，password表示密码，passlen表示密码字符串长度。
        /// </code>
        /// </summary>
        /// <param name="nMessage"></param>
        /// <param name="hWnd"></param>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern void MP4_ServerCheckPassword(CheckPassword cp);

        /// <summary>
        /// 往发送缓存写数据。 
        ///     网络开发包通过这个接口获得板卡的数据。 
        /// 说明： 
        ///     现在开发包内部不会直接调用StartVideoCapture和StopVideoCapture函数，而是通过StartCap和StopCap
        ///     来启动和停止捕获数据。如果用户调用StopVideoCapture或者停止调用MP4_ServerWriteData都会使客户
        ///     端无法收到数据。
        /// <code>
        /// 8. void  __stdcall  MP4_ServerWriteData(DWORD nChannel,UCHAR *pPacketBuffer, DWORD  nPacketSize, int frameType, int breakable); 
        /// typedef unsigned long       DWORD;
        /// typedef unsigned char UCHAR;
        /// </code>
        /// </summary>
        /// <param name="nChannel">通道号。</param>
        /// <param name="pPacketBuffer">缓冲区指针。</param>
        /// <param name="nPacketSize">缓冲区长度。 </param>
        /// <param name="frameType">帧类型。</param>
        /// <param name="breakable">ReadStreamData的返回值</param>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern void MP4_ServerWriteData(ulong nChannel, string pPacketBuffer, ulong nPacketSize, int frameType, int breakable);

        /// <summary>
        /// 设置启动捕获的回调。
        /// <code>
        /// 9.  void  __stdcall  MP4_ServerSetStart(void(CALLBACK *StartCap)(int nChannel)); 
        /// StartCap函数说明：用户实现这个函数，只需要调用StartVideoCapture。 
        /// nChannel表示通道号。 
        /// </code>
        /// </summary>
        /// <param name="sc"></param>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern void MP4_ServerSetStart(StartCap sc);

        /// <summary>
        /// 设置停止捕获的回调。
        /// <code>
        /// 10.  void  __stdcall  MP4_ServerSetStop(void(CALLBACK *StopCap)(int nChannel)); 
        /// StopCap函数说明：用户实现这个函数，只需要调用StopVideoCapture。 
        /// nChannel表示通道号。 
        /// </code>
        /// </summary>
        /// <param name="sc"></param>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern void MP4_ServerSetStop(StopCap sc);

        /// <summary>
        /// 读取客户端消息。
        ///     读取客户端MP4_ClientCommandtoServer函数发送过来的消息(不超过900字节)，消息的内容和长度由用户自己定义。 
        /// <code>
        /// 11.  void  __stdcall  MP4_ServerReadLastMessage(char *m_sIP,char *m_sCommand,WORD *m_wLen); 
        /// </code>
        /// </summary>
        /// <param name="m_sIP">消息来自哪个IP地址</param>
        /// <param name="m_sCommand">消息缓冲区指针。</param>
        /// <param name="m_wLen">消息实际长度。 </param>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern void MP4_ServerReadLastMessage(StringBuilder m_sIP, StringBuilder m_sCommand, out ushort m_wLen);

        /// <summary>
        /// 给客户端发送消息字符串。
        /// <code>
        /// 12.  BOOL  __stdcall  MP4_ServerStringToClient(LPCTSTR m_lAddrIP,char *m_sCommand, WORD m_wLen); 
        /// typedef LPCSTR LPCTSTR;
        /// typedef CONST CHAR *LPCSTR
        /// </code>
        /// </summary>
        /// <param name="m_lAddrIP">客户端IP地址。</param>
        /// <param name="m_sCommand">消息缓冲区指针。</param>
        /// <param name="m_wLen">消息实际长度。</param>
        /// <returns>返回TRUE表示成功，返回FALSE表示失败</returns>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern bool MP4_ServerStringToClient(string m_lAddrIP, string m_sCommand, ushort m_wLen);

        /// <summary>
        /// 对nChannel通道的网络连接进行复位。
        ///     如果需要单独终止该通道目前的客户端连接，或者该通道目前的网络状态处于异常状况，都可以调用该函
        ///     数。该函数对任何板卡SDK对应操作都不会有任何影响。
        /// <code>
        /// 13.  void  __stdcall  MP4_ServerResetChannel(DWORD nChannel)；
        /// </code>
        /// </summary>
        /// <param name="nChannel">通道号。</param>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern void MP4_ServerResetChannel(ulong nChannel);

        /// <summary>
        /// 设置服务端的网络端口号和客户端的网络端口号。
        ///     服务端使用端口如下(假设dServerPort=5050)：
        ///     TCP只需要占用5050。
        ///     UDP和多播需要占用5050、5060----(5060+2*通道个数)。
        ///     
        ///     客户端使用端口如下(假设dClientPort =6050)：
        ///     TCP和多播只需要占用6057。
        ///     UDP需要占用6057、6060----(6060+2*窗口个数)。
        /// <code>
        /// 14.  BOOL  __stdcall  MP4_ServerSetNetPort(WORD dServerPort,WORD dClientPort);
        /// </code>
        /// </summary>
        /// <param name="dServerPort">服务端的起始网络端口号。必须和函数MP4_ClientSetNetPort的dServerPort参数相同。</param>
        /// <param name="dClientPort">客户端的网络端口号。必须和函数MP4_ClientSetNetPort的dClientPort参数相同。</param>
        /// <returns>返回TRUE表示成功，返回FALSE表示失败。</returns>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern int MP4_ServerSetNetPort(ushort dServerPort, ushort dClientPort);

        /// <summary>
        /// 设置多播的TTL参数。
        ///     注意： 如果您不调用函数MP4_ServerSetTTL，默认TTL值是32。
        ///     MP4_ServerSetTTL设置的值在MP4_ServerStop之后会失效，恢复成默认的32，所以您如果需要指定TTL
        ///     大小，每次在MP4_ServerStart之前都需要调用MP4_ServerSetTTL。
        /// <code>
        /// 15.  BOOL  __stdcall  MP4_ServerSetTTL(unsigned char cTTLVal); 
        /// </code>
        /// </summary>
        /// <param name="cTTLVal">TTL值。1-255。</param>
        /// <returns>返回TRUE表示成功，返回FALSE表示失败。</returns>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern int MP4_ServerSetTTL(byte cTTLVal);

        /// <summary>
        /// 设置发送缓冲区大小。
        ///     注意： 如果您不调用函数MP4_ServerSetBufNum，默认缓冲区是30。
        ///     MP4_ServerSetBufNum设置的值在MP4_ServerStop之后会失效，恢复成默认的30，所以您如果需要指定
        ///     缓冲区大小，每次在MP4_ServerStart之前都需要调用MP4_ServerSetBufNum。
        /// <code>
        /// 16.  BOOL  __stdcall  MP4_ServerSetBufNum(DWORD nChannel,WORD dBufNum); 
        /// </code>
        /// </summary>
        /// <param name="nChannel">通道号。</param>
        /// <param name="dBufNum">缓冲区大小。单位是8K，如果dBufNum=10，那么缓冲区大小为80K，建议设置成30。范围：10-100。</param>
        /// <returns>返回TRUE表示成功，返回FALSE表示失败。</returns>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern int MP4_ServerSetBufNum(ushort nChannel, ushort dBufNum);

        /// <summary>
        /// 设置自动调节帧率的回调函数，只在电话线连接中使用。
        ///     在回调函数SetIBP中只调用SetIBPMode函数
        /// <code>
        /// 17.  void  __stdcall  MP4_ServerSetIBPMode(void(CALLBACK *SetIBP)(int nChannel,int framerat));
        /// </code>
        /// </summary>
        /// <param name="setIBP"></param>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern void MP4_ServerSetIBPMode(SetIBP setIBP);

        /// <summary>
        /// 设置连接客户端的等待时间和尝试次数。
        ///     说明：当服务端给客户端发送消息的时候，需要等待客户端应答，以保证客户端收到消息，如果失败，还
        ///     可以继续尝试dTrynum-1次发送。
        ///     注意：
        ///     1>如果这两个参数设得太小，当网络繁忙或客户端繁忙的时候，可能会导致发送消息失败；如果这两个参
        ///     数设得太大，当试图连接的客户端不存在(IP地址错误、或者关机、或者没有启动客户端软件)，消息发送
        ///     函数会等待很久才返回。
        ///     2>这两个参数设置之后一直有效，直到下次调用该函数修改。
        /// <code>
        /// 18.  BOOL  __stdcall  MP4_ServerSetWait(DWORD dEachWaitTime,DWORD dTrynum); 
        /// </code>
        /// </summary>
        /// <param name="dEachWaitTime">等待时间。单位是毫秒，范围300-10000。如果不调用这个函数，默认是2000。</param>
        /// <param name="dTrynum">尝试次数。范围1-50。如果不调用这个函数，默认是2次。</param>
        /// <returns>返回TRUE表示成功，返回FALSE表示失败。</returns>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern bool MP4_ServerSetWait(ulong dEachWaitTime, ulong dTrynum);

        /// <summary>
        /// 给连接在指定通道上的所有客户端发送消息字符串。
        /// <code>
        /// 19.  BOOL  __stdcall  MP4_ServerStringToClient_other     (char nChannel,char *m_sCommand, WORD m_wLen);  
        /// </code>
        /// </summary>
        /// <param name="nChannel">通道号。</param>
        /// <param name="m_sCommand">消息缓冲区指针。</param>
        /// <param name="m_wLen">消息实际长度。</param>
        /// <returns>返回TRUE表示成功，返回FALSE表示失败。 </returns>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern bool MP4_ServerStringToClient_other(int nChannel, string m_sCommand, ushort m_wLen);

        /// <summary>
        /// 动态切换通道数据类型。
        ///     注意：调用MP4_ServerChangeChanType之前必须调用StopVideoCapture。（详见DEMO） 
        /// <code>
        /// 20.  BOOL  __stdcall  MP4_ServerChangeChanType(char nChannel,char cType); 
        /// </code>
        /// </summary>
        /// <param name="nChannel">通道号。 </param>
        /// <param name="cType">通道数据类型，是否支持电话线连接（NORMAL或者DIALING）</param>
        /// <returns>返回TRUE表示成功，返回FALSE表示失败。</returns>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern bool MP4_ServerChangeChanType(char nChannel, char cType);

        /// <summary>
        /// 设置回调，重新生成一个I帧。 
        /// <code>
        /// 21.  void  __stdcall  MP4_ServerSetCapIFrame(void(CALLBACK *MakeIFrame) (int port)); 
        /// </code>
        /// </summary>
        /// <param name="mf"></param>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern void MP4_ServerSetCapIFrame(MakeIFrame mf);

        /// <summary>
        /// 获取版本号。
        /// <code>
        /// 22.  DWORD  __stdcall  MP4_ServerGetSdkVersion(); 
        /// </code>
        /// </summary>
        /// <returns></returns>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern ulong MP4_ServerGetSdkVersion();

        /// <summary>
        /// 设置每个通道的多播组地址和网络端口号。
        ///     说明：
        ///     1>目前的多播组采用了两种方式：一是开发包内部分配，用户不需要考虑多播组参数细节，默认采用这种
        ///     方式；另一种是调用MP4_ServerCastGroup设置多播组参数。
        ///     2>用户可以通过MP4_ServerCastGroup修改参数bSet，在两种方式之间做切换。
        ///     每个多播组会占用wPort开始的4个端口。
        /// <code>
        /// 23.  BOOL  __stdcall  MP4_ServerCastGroup(BOOL bSet,DWORD dChannel, char *sIP, WORD wPort); 
        /// </code>
        /// </summary>
        /// <param name="bSet">是否使用自定义的多播组地址和网络端口号。TRUE表示使用，FALSE表示仍然沿用开发包内部自动分配的多播地址和网络端口号。 </param>
        /// <param name="dChannel">通道号。每个通道号对应的多播组地址和网络端口号都是单独设置的。</param>
        /// <param name="sIP">多播组地址。</param>
        /// <param name="wPort">多播组网络端口号</param>
        /// <returns>返回TRUE表示成功，返回FALSE表示失败。 </returns>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern bool MP4_ServerCastGroup(bool bSet, ulong dChannel, string sIP, ushort wPort);

        /// <summary>
        /// 设置每个通道的最大用户数量。
        /// <code>
        /// 24.  BOOL  __stdcall  MP4_ServerMaxUser(DWORD dwChannel,int nMaxNum)
        /// </code>
        /// </summary>
        /// <param name="dwChannel">通道号。</param>
        /// <param name="nMaxNum">用户数量。</param>
        /// <returns>返回TRUE表示成功，返回FALSE表示失败。</returns>
        [DllImport(@"\HK\HikServer.dll")]
        public static extern int MP4_ServerMaxUser(uint dwChannel, int nMaxNum);

        /// <summary>
        /// 往发送缓存写数据。网络开发包通过这个接口获得板卡的数据。 
        ///     说明：
        ///     现在开发包内部不会直接调用StartVideoCapture和StopVideoCapture函数，而是通过StartCap
        ///     和StopCap来启动和停止捕获数据。如果用户调用StopVideoCapture或者停止调用MP4_ServerWriteDataEx
        ///     都会使客户端无法收到数据。原来的MP4_ServerWriteData内部调用MP4_ServerWriteDataEx完成相关功能。
        /// <code>
        /// 25.  void  __stdcall  MP4_ServerWriteDataEx(DWORD nPort,UCHAR *pPacketBuffer,DWORD nPacketSize,int frameType,int breakable,int nImgFormat)
        /// </code>
        /// </summary>
        /// <param name="nPort">通道号。</param>
        /// <param name="pPacketBuffer">缓冲区指针。</param>
        /// <param name="nPacketSize">缓冲区长度。</param>
        /// <param name="frameType">帧类型。</param>
        /// <param name="breakable">ReadStreamData的返回值</param>
        /// <param name="nImgFormat">
        ///     图像格式。nImgFormat 为0 时表示发送主通道的音视频数据
        ///     nImgFormat 为1 时表示发送子通道的音视频数据。
        /// </param>
        [DllImport(@"\HK\HikServer.dll")]
        //public unsafe static extern void MP4_ServerWriteDataEx(uint nPort, void* pPacketBuffer, uint nPacketSize, int frameType, int breakable, int nImgFormat);
        public static extern void MP4_ServerWriteDataEx(int nPort, IntPtr pPacketBuffer, int nPacketSize, int frameType, int breakable, int nImgFormat);
        //public static extern void MP4_ServerWriteDataEx(uint nPort, byte[] pPacketBuffer, uint nPacketSize, int frameType, int breakable, int nImgFormat);

      #endregion
    }
}
/*
 * 修改记录 

      1.      2009-8-19，修改了MP4_ServerReadLastMessage，非常感谢 阿斯顿啊 的贡献！
            修正前：public static extern void MP4_ServerReadLastMessage(string m_sIP, string m_sCommand, out ushort m_wLen);
            修正后：public static extern void MP4_ServerReadLastMessage(StringBuilder m_sIP, StringBuilder m_sCommand, out ushort m_wLen);
 */